/**************************************************************************
  This is a library for several Adafruit displays based on ST77* drivers.

  Works with the Adafruit TFT Gizmo
    ----> http://www.adafruit.com/products/4367

  Check out the links above for our tutorials and wiring diagrams.

  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.
  MIT license, all text above must be included in any redistribution
 **************************************************************************/
#define USE_ST7789 1


#include <Arduino.h>

#if USE_ST7789
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <SPI.h>



#include "lvgl.h"
#include "demos/widgets/lv_demo_widgets.h"

#endif

#include "shell.h"

Shell shell;
char shellBuffer[512];
short shellRead(char *data, unsigned short len)
{
    //return uart_read_bytes(0, (uint8_t *)data, len, portMAX_DELAY);    
    return Serial.readBytes((uint8_t *)data, len);
}

short shellWrite(char *data, unsigned short len)
{
    //return uart_write_bytes(0, (const char *)data, len);
    return Serial.write((const char *)data, len);
}


#if USE_ST7789
// Because of the limited number of pins available on the Circuit Playground Boards
// Software SPI is used
#define TFT_CS        5
#define TFT_RST       3 // Or set to -1 and connect to Arduino RESET pin
#define TFT_DC        4
#define TFT_BACKLIGHT 6 // Display backlight pin
#define TFT_MOSI      2  // Data out
#define TFT_MISO      25
#define TFT_SCLK      1  // Clock out

//使用硬件SPI
SPIClass* spi = &SPI;
Adafruit_ST7789 *tft;



//核心任务
//定期调用 lv_tick_inc(tick_period) 函数并提供以毫秒为单位的调用周期。例如，lv_tick_inc(1) 每毫秒调用一次
static void lv_tick_inc_task( void *pvParameters );
//时间并不重要，但它应该是大约 5 毫秒以保持系统响应
static void lv_timer_handler_task( void *pvParameters );


//绘制缓冲区
/*A static or global variable to store the buffers*/
static lv_disp_draw_buf_t disp_buf;

#define MY_DISP_HOR_RES 240      //一行像素
#define MY_DISP_VER_RES 240      //一列像素
/*Static or global buffer(s). The second buffer is optional*/
static lv_color_t buf_1[MY_DISP_HOR_RES * (MY_DISP_VER_RES)/10];   //屏幕1/10
static lv_color_t buf_2[MY_DISP_HOR_RES * (MY_DISP_VER_RES)/10];

//Display driver（显示驱动程序）
static lv_disp_drv_t disp_drv;          /*A variable to hold the drivers. Must be static or global.*/
static void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);//显示回调


#endif

void setup(void) {
  Serial.begin(115200);
#if USE_ST7789
  ///////////////////////////////////////tft/////////////////////////////////////////
  //硬件SPI
  spi = &SPI;
  tft = new Adafruit_ST7789(spi, TFT_CS, TFT_DC, TFT_RST);

  spi->begin(TFT_SCLK,TFT_MISO,TFT_MOSI);
  pinMode(TFT_BACKLIGHT, OUTPUT);
  digitalWrite(TFT_BACKLIGHT, HIGH); // Backlight on
  tft->init(MY_DISP_HOR_RES, MY_DISP_VER_RES);                // Init ST7789 240x240
  tft->setRotation(2);

  //////////////////////////////lvgl///////////////////////////////////////////////
  lv_init();

  //初始化lvgl 绘制缓存区
  lv_disp_draw_buf_init(&disp_buf, buf_1, buf_2, MY_DISP_HOR_RES * (MY_DISP_HOR_RES) / 10);
  //Display driver（显示驱动程序）
  lv_disp_drv_init(&disp_drv);            /*Basic initialization*/
  disp_drv.draw_buf = &disp_buf;          /*Set an initialized buffer*/
  disp_drv.flush_cb = my_flush_cb;        /*Set a flush callback to draw to the display*/
  disp_drv.hor_res = MY_DISP_HOR_RES;                 /*Set the horizontal resolution in pixels*/
  disp_drv.ver_res = MY_DISP_VER_RES;                 /*Set the vertical resolution in pixels*/

  lv_disp_t * disp;
  disp = lv_disp_drv_register(&disp_drv); /*Register the driver and save the created display objects*/

#endif

  shell.read = shellRead;
  shell.write = shellWrite;
  shellInit(&shell, shellBuffer, 512);

#if USE_ST7789
  // lvgl核心任务
  xTaskCreatePinnedToCore(
    lv_tick_inc_task
    ,  "lv_tick_inc_task"   // A name just for humans
    ,  10240  // This stack size can be checked & adjusted by reading the Stack Highwater
    ,  NULL
    ,  2  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,  NULL 
    ,  ARDUINO_RUNNING_CORE);
      // Now set up two tasks to run independently.
  xTaskCreatePinnedToCore(
    lv_timer_handler_task
    ,  "lv_timer_handler_task"   // A name just for humans
    ,  10240  // This stack size can be checked & adjusted by reading the Stack Highwater
    ,  NULL
    ,  1  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,  NULL 
    ,  ARDUINO_RUNNING_CORE);

  //lv_demo_widgets();
  lv_demo_stress();

#endif

  xTaskCreatePinnedToCore(
    shellTask
    ,  "shell"   // A name just for humans
    ,  1024*2  // This stack size,栈太小容易崩溃
    ,  &shell
    ,  1  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,  NULL 
    ,  ARDUINO_RUNNING_CORE);
}

void loop() {
    vTaskDelay(500 / portTICK_PERIOD_MS);
    //Serial.print("loop....\r\n");
}

#if USE_ST7789
static void lv_tick_inc_task( void *pvParameters )
{
  while (1)
  {
    /* code */
    vTaskDelay(5/portTICK_PERIOD_MS);
    lv_tick_inc(5);
  }
  
}
static void lv_timer_handler_task( void *pvParameters )
{
  while (1)
  {
    /* code */
    lv_timer_handler();
    vTaskDelay(5/portTICK_PERIOD_MS);
  }
}
static void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{

    /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one
     *`put_px` is just an example, it needs to implemented by you.*/
    tft->drawRGBBitmap(area->x1,area->y1,(uint16_t*)color_p,area->x2-area->x1+1,area->y2-area->y1+1); 
    lv_disp_flush_ready(disp_drv);
}

#endif